package com.xqboss.apps.service.user;

import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.domain.box.Box;
import com.xqboss.apps.domain.box.BoxNft;
import com.xqboss.apps.domain.nft.NftNumber;
import com.xqboss.apps.domain.order.OrderItem;
import com.xqboss.apps.domain.user.UserBox;
import com.xqboss.apps.domain.user.UserBoxNft;
import com.xqboss.apps.enums.market.UserBoxNftStatusEnum;
import com.xqboss.apps.mapper.user.UserBoxNftMapper;
import com.xqboss.apps.service.box.BoxService;
import com.xqboss.apps.service.nft.NftNumberService;
import com.xqboss.apps.service.nft.NftService;
import com.xqboss.apps.vo.user.UserNFTItemVo;
import com.xqboss.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
public class UserBoxNftService extends ServiceImpl<UserBoxNftMapper, UserBoxNft> {

    @Autowired
    private NftNumberService nftNumberService;

    @Autowired
    private NftService nftService;

    @Autowired
    @Lazy
    private BoxService boxService;

    @Transactional(rollbackFor = Exception.class)
    public void saveUserBoxNftList(Long orderMainId, Long orderItemId, UserBox userBox, List<Pair<BoxNft, List<NftNumber>>> nftList, LocalDateTime saleableTime){
        List<UserBoxNft> list = new ArrayList<>();
        for (Pair<BoxNft, List<NftNumber>> pair : nftList) {
            for (NftNumber nftNumber : pair.getValue()) {
                UserBoxNft userBoxNft = new UserBoxNft();
                userBoxNft.setUserBoxId(userBox.getId());
                userBoxNft.setBoxId(userBox.getBoxId());
                userBoxNft.setBoxNftId(pair.getKey().getId());
                userBoxNft.setOrderMainId(orderMainId);
                userBoxNft.setOrderItemId(orderItemId);
                userBoxNft.setNftId(nftNumber.getNftId());
                userBoxNft.setNftNumberId(nftNumber.getId());
                userBoxNft.setNftType(nftService.getById(nftNumber.getNftId()).getNftType());
                userBoxNft.setStatus(UserBoxNftStatusEnum.NORMAL);
                userBoxNft.setSaleableTime(saleableTime);
                list.add(userBoxNft);
            }
        }
        Collections.shuffle(list);
        boolean save = saveBatch(list);
        if(!save){
            throw new ServiceException("操作失败！");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public List<UserNFTItemVo> openBox(UserBox userBox, Integer quantity) {
        List<UserBoxNft> list = list(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, userBox.getId())
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                .last("LIMIT " + quantity));
        if(list.size() != quantity){
            throw new ServiceException("开箱库存不足！");
        }

        List<UserNFTItemVo> userNFTItemVoList = new ArrayList<>();
        // 修改开箱状态
        for (UserBoxNft userBoxNft : list) {
            boolean update = update(new LambdaUpdateWrapper<UserBoxNft>()
                    .set(UserBoxNft::getStatus, UserBoxNftStatusEnum.OPENED)
                    .eq(UserBoxNft::getId, userBoxNft.getId()));
            if(!update){
                throw new ServiceException("开箱失败！");
            }
            UserNFTItemVo userNFTItemVo = nftNumberService.dispatchOpenBoxNftNumber(userBox, userBoxNft);
            userNFTItemVoList.add(userNFTItemVo);
        }
        return userNFTItemVoList;
    }


    @Transactional(rollbackFor = Exception.class)
    public List<UserBoxNft> moveBoxToUser(OrderItem orderItem, UserBox sellerUserBox, UserBox buyerUserBox, Integer count) {
        List<UserBoxNft> list = list(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, sellerUserBox.getId())
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.SALEABLE)
                .last("LIMIT " + count));
        if(list.size() != count){
            log.error("用户宝箱库存不足，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("库存不足！");
        }
        boolean remove = removeByIds(list);
        if(!remove){
            log.error("删除用户宝箱失败，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("操作失败！");
        }
        Box box = boxService.getBoxById(sellerUserBox.getBoxId());
        // 计算可售时间
        LocalDateTime saleableTime = LocalDateTime.now().plusMinutes(box.getCoolingTime());
        for (UserBoxNft userBoxNft : list) {
            userBoxNft.setId(null);
            userBoxNft.setUserBoxId(buyerUserBox.getId());
            userBoxNft.setOrderMainId(orderItem.getOrderMainId());
            userBoxNft.setOrderItemId(orderItem.getId());
            userBoxNft.setStatus(UserBoxNftStatusEnum.NORMAL);
            userBoxNft.setSaleableTime(saleableTime);
        }
        boolean save = saveBatch(list);
        if(!save){
            log.error("保存用户宝箱失败，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("操作失败！");
        }
        return list;
    }


    @Transactional(rollbackFor = Exception.class)
    public List<UserBoxNft> moveBoxToUser(UserBox sellerUserBox, UserBox buyerUserBox, Integer count) {
        List<UserBoxNft> list = list(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, sellerUserBox.getId())
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                .last("LIMIT " + count));
        if(list.size() != count){
            log.error("用户宝箱库存不足，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("库存不足！");
        }
        boolean remove = removeByIds(list);
        if(!remove){
            log.error("删除用户宝箱失败，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("操作失败！");
        }
        Box box = boxService.getBoxById(sellerUserBox.getBoxId());
        // 计算可售时间
        LocalDateTime saleableTime = LocalDateTime.now().plusMinutes(box.getCoolingTime());
        for (UserBoxNft userBoxNft : list) {
            userBoxNft.setId(null);
            userBoxNft.setUserBoxId(buyerUserBox.getId());
            userBoxNft.setStatus(UserBoxNftStatusEnum.NORMAL);
            userBoxNft.setSaleableTime(saleableTime);
        }
        boolean save = saveBatch(list);
        if(!save){
            log.error("保存用户宝箱失败，请处理异常: {}", JSON.toJSONString(sellerUserBox));
            throw new ServiceException("操作失败！");
        }
        return list;
    }

    /**
     * 统计用户宝箱当前可售数量
     * @param userBoxId
     * @return
     */
    public int countUsableStock(Long userBoxId) {
        return (int) count(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, userBoxId)
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                .le(UserBoxNft::getSaleableTime, LocalDateTime.now()));
    }

    /**
     * 统计用户宝箱当前冻结数量
     * @param userBoxId
     * @return
     */
    public int countFreezeStock(Long userBoxId) {
        return (int) count(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, userBoxId)
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                .gt(UserBoxNft::getSaleableTime, LocalDateTime.now()));
    }

    /**
     * 取消售卖
     * @param userBoxId
     * @param quantity
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelSell(Long userBoxId, Integer quantity) {
        List<UserBoxNft> list = list(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, userBoxId)
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.SALEABLE)
                .last("LIMIT " + quantity));
        if(list.size() != quantity){
            log.error("用户宝箱库存不足，请处理异常: userBoxId:{}  quantity: {}", userBoxId, quantity);
            throw new ServiceException("库存不足！");
        }
        for (UserBoxNft userBoxNft : list) {
            boolean update = update(new LambdaUpdateWrapper<UserBoxNft>()
                    .eq(UserBoxNft::getId, userBoxNft.getId())
                    .set(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                    .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.SALEABLE));
            if(!update){
                throw new ServiceException("取消失败！");
            }
        }
    }

    /**
     * 取消售卖
     * @param userBoxId
     * @param quantity
     */
    @Transactional(rollbackFor = Exception.class)
    public void addSell(Long userBoxId, Integer quantity) {
        List<UserBoxNft> list = list(new LambdaQueryWrapper<UserBoxNft>()
                .eq(UserBoxNft::getUserBoxId, userBoxId)
                .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL)
                .last("LIMIT " + quantity));
        if(list.size() != quantity){
            log.error("用户宝箱库存不足，请处理异常: userBoxId:{}  quantity: {}", userBoxId, quantity);
            throw new ServiceException("库存不足！");
        }
        for (UserBoxNft userBoxNft : list) {
            boolean update = update(new LambdaUpdateWrapper<UserBoxNft>()
                    .eq(UserBoxNft::getId, userBoxNft.getId())
                    .set(UserBoxNft::getStatus, UserBoxNftStatusEnum.SALEABLE)
                    .eq(UserBoxNft::getStatus, UserBoxNftStatusEnum.NORMAL));
            if(!update){
                throw new ServiceException("上架失败！");
            }
        }
    }
}
